#pragma once

class ISocketHandler
{
	std::vector<uint8> mReadBuffer, mWriteBuffer;
	Mutex mWriteLock, mReadLock;
protected:
	void pushWriteBuffer(const void* data, uint32 numBytes) {
		mWriteLock.acquire();
		mWriteBuffer.insert(mWriteBuffer.end(), (uint8*)data, ((uint8*)data) + numBytes);
		mWriteLock.release();
	}

	void pushWriteBuffer(const std::vector<uint8>& data) {
		pushWriteBuffer(&data.front(), data.size());
	}

	Socket* mSocket;
	bool mUseDirectNotify;

public:
	ISocketHandler() : mUseDirectNotify(false) {
	}

	virtual void clientConnected(Socket* socket) = 0;
	Socket* getSocket() { return mSocket; }
	void onDataAvailable(const std::vector<uint8>& data) {
		lockReadBuffer();
		mReadBuffer.insert(mReadBuffer.end(), data.begin(), data.end());
		unlockReadBuffer();
	}

	virtual void notifyDataRead(const std::vector<uint8>& data) {
	};

	bool getDirectNotify() { return mUseDirectNotify; }

	void lockReadBuffer() {
		mReadLock.acquire();
	}

	void unlockReadBuffer() {
		mReadLock.release();
	}

	void lockWriteBuffer() {
		mWriteLock.acquire();
	}

	void unlockWriteBuffer() {
		mWriteLock.release();
	}

	void popWriteBuffer(std::vector<uint8>& buffer) {
		lockWriteBuffer();
		if(buffer.size() <= mWriteBuffer.size()) {
			std::copy(mWriteBuffer.begin(), mWriteBuffer.begin() + buffer.size(), buffer.begin());
			mWriteBuffer.erase(mWriteBuffer.begin(), mWriteBuffer.begin() + buffer.size());
		}
		unlockWriteBuffer();
	}

	uint32 getReadBufferSize() const {
		return mReadBuffer.size();
	}

	uint32 getWriteBufferSize() const {
		return mWriteBuffer.size();
	}

	void sendData(const void* data, uint32 numBytes) {
		pushWriteBuffer(data, numBytes);
	}

	template<typename T>
	void sendData(const T& data) {
		pushWriteBuffer(&data, sizeof(T));
	}

	void sendData(ByteBuffer& bb) {
		const std::vector<uint8>& data = bb.getBuffer();
		if(data.size() == 0)
			return;

		sendData(&data.front(), data.size());
	}
};